(in-ns 'miked.grid)
(clojure/refer 'clojure)

(defn apportion [lengths items]
  "Splits the given items into lists of the given lengths"
  (when (and lengths items)
    (cons (take (first lengths) items)
	  (apportion (rest lengths) (drop (first lengths) items)))))

(defn grid [lengths items]
  (apply vector 
	 (map (fn [x] (apply vector x)) 
	      (apportion lengths items))))

(defn coord [x y]
  {:x x :y y})

(def #^{:private true}
  *test-grid*
  (grid '(3 3 3)
	(range 0 10)))

(defn #^{:test (fn [] (assert (= 8 (item-at *test-grid* (coord 1 2)))))}
  item-at [grid pt]
  (get (get grid (:x pt)) (:y pt)))

(defn #^{:test 
  (fn []
      (assert (= '(0 4 8) 
		 (items-at *test-grid*
			   (map (partial apply coord)
				'((0 0) (1 1) (2 2)))))))
  }
  items-at [grid points]
  (map (fn [point] (item-at grid point)) points))

(defn north [pt] (coord (dec (:x pt))     (:y pt)))
(defn south [pt] (coord (inc (:x pt))     (:y pt)))
(defn east  [pt] (coord      (:x pt) (inc (:y pt))))
(defn west  [pt] (coord      (:x pt) (dec (:y pt))))

(defn south-east [pt] (south (east pt)))
(defn north-east [pt] (north (east pt)))
(defn south-west [pt] (south (west pt)))
(defn north-west [pt] (north (west pt)))

(defn #^{:test (fn [] (assert (= (list (coord 0 0) (coord 1 1) (coord 2 2))
				 (grid-line (coord 0 0) south-east 3))))
  }
  grid-line [start dir len]
  (when-not (zero? len)
    (cons start (grid-line (dir start) dir (dec len)))))

(defn #^{:private true}
  grid-map-cols [f g p]
  (when (item-at g p)
    (cons (f g p) (grid-map-cols f g (east p)))))

(defn #^{:private true} 
  grid-map-rows [f g p]
  (when (item-at g p)
    (cons (apply vector (grid-map-cols f g p))
	  (grid-map-rows f g (south p)))))
		  
(defn grid-map [f g]
  (apply vector (grid-map-rows f g '(0 0))))

